﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using EnvDTE;
using EnvDTE80;
using Extensibility;
using Microsoft.VisualStudio.CommandBars;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Vsip;

namespace VSLoadTestCustomReportName
{
    /// <summary>The object for implementing an Add-in.</summary>
    /// <seealso class='IDTExtensibility2' />
    public class Connect : IDTExtensibility2, IDTCommandTarget
    {
        private const string reportName = "Results";

        private const string settingsName = "Settings";

        private const int reportImageIndex = 162;

        private const int settingsImageIndex = 3000;
        private AddIn _addInInstance;
        private DTE2 _applicationObject;

        private ReportEntryForm reportEntryForm;
        private CommandBarControl testResultsContextMenuCommandBarControl;
        private CommandBarPopup testResultsContextMenuCommandBarPopup;

        #region ConnectionManagement Methods

        /// <summary>
        ///     Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in
        ///     is being loaded.
        /// </summary>
        /// <param term='application'>Root object of the host application.</param>
        /// <param term='connectMode'>Describes how the Add-in is being loaded.</param>
        /// <param term='addInInst'>Object representing this Add-in.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            _applicationObject = (DTE2) application;
            _addInInstance = (AddIn) addInInst;

            try
            {
                switch (connectMode)
                {
                    case ext_ConnectMode.ext_cm_UISetup:
                        // Do nothing for this add-in with temporary user interface                        
                        CreateCommands();
                        break;
                    case ext_ConnectMode.ext_cm_Startup:
                        // The add-in was marked to load on startup
                        // Do nothing at this point because the IDE may not be fully initialized
                        // Visual Studio will call OnStartupComplete when fully initialized                        
                        break;
                    case ext_ConnectMode.ext_cm_AfterStartup:
                        // The add-in was loaded by hand after startup using the Add-In Manager
                        // Initialize it in the same way that when is loaded on startup                        
                        InitializeAddIn();
                        break;
                }
            }
            catch (Exception exception)
            {
                ShowMessage(exception.ToString());
            }
        }

        /// <summary>
        ///     Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the
        ///     Add-in is being unloaded.
        /// </summary>
        /// <param term='disconnectMode'>Describes how the Add-in is being unloaded.</param>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
            try
            {
                switch (disconnectMode)
                {
                    case ext_DisconnectMode.ext_dm_UserClosed:
                    case ext_DisconnectMode.ext_dm_HostShutdown:
                        RecreateCommands();
                        break;
                }

                if (testResultsContextMenuCommandBarPopup != null)
                {
                    try
                    {
                        testResultsContextMenuCommandBarPopup.Delete(true);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
            catch (Exception exception)
            {
                ShowMessage(exception.ToString());
            }
        }

        /// <summary>
        ///     Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the
        ///     collection of Add-ins has changed.
        /// </summary>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnAddInsUpdate(ref Array custom)
        {
        }

        /// <summary>
        ///     Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the
        ///     host application has completed loading.
        /// </summary>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnStartupComplete(ref Array custom)
        {
            try
            {
                InitializeAddIn();
            }
            catch (Exception exception)
            {
                ShowMessage(exception.ToString());
            }
        }

        /// <summary>
        ///     Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host
        ///     application is being unloaded.
        /// </summary>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnBeginShutdown(ref Array custom)
        {
            try
            {
                RecreateCommands();

                if (testResultsContextMenuCommandBarPopup != null)
                {
                    testResultsContextMenuCommandBarPopup.Delete(true);
                }
            }
            catch (Exception exception)
            {
                ShowMessage(exception.ToString());
            }
        }

        #endregion

        /// <summary>
        ///     Implements the QueryStatus method of the IDTCommandTarget interface. This is called when the command's
        ///     availability is updated
        /// </summary>
        /// <param term='commandName'>The name of the command to determine state for.</param>
        /// <param term='neededText'>Text that is needed for the command.</param>
        /// <param term='status'>The state of the command in the user interface.</param>
        /// <param term='commandText'>Text requested by the neededText parameter.</param>
        /// <seealso class='Exec' />
        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status,
            ref object commandText)
        {
            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
            {
                if (commandName == _addInInstance.ProgID + "." + reportName)
                {
                    status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
                }
                if (commandName == _addInInstance.ProgID + "." + settingsName)
                {
                    status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
                }
                status = vsCommandStatus.vsCommandStatusUnsupported;
            }
        }

        /// <summary>Implements the Exec method of the IDTCommandTarget interface. This is called when the command is invoked.</summary>
        /// <param term='commandName'>The name of the command to execute.</param>
        /// <param term='executeOption'>Describes how the command should be run.</param>
        /// <param term='varIn'>Parameters passed from the caller to the command handler.</param>
        /// <param term='varOut'>Parameters passed from the command handler to the caller.</param>
        /// <param term='handled'>Informs the caller if the command was handled or not.</param>
        /// <seealso class='Exec' />
        public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut,
            ref bool handled)
        {
            handled = false;
            if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
            {
                if (commandName == _addInInstance.ProgID + "." + reportName)
                {
                    handled = true;
                    List<TrxFile> fileNames = GetTrxFiles();
                    reportEntryForm = new ReportEntryForm(fileNames);
                    reportEntryForm.ShowDialog();
                }
                if (commandName == _addInInstance.ProgID + "." + settingsName)
                {
                    handled = true;
                    ShowMessage("This menu item is reserved for future releases.", 3);
                }
            }
        }

        #region User-Defined Functions and Derived/Implemented Functions

        /// <summary>
        ///     Creates the commands.
        /// </summary>
        private void CreateCommands()
        {
            CreateCommand(reportName, reportName, reportName, reportImageIndex, null);
            CreateCommand(settingsName, settingsName, settingsName, settingsImageIndex, null);
        }

        /// <summary>
        ///     Creates the command.
        /// </summary>
        /// <param name="commandShortName">Short name of the command.</param>
        /// <param name="commandCaption">The command caption.</param>
        /// <param name="commandTooltip">The command tooltip.</param>
        /// <param name="commandImage">The command image.</param>
        /// <param name="bindings">The bindings.</param>
        private void CreateCommand(string commandShortName, string commandCaption, string commandTooltip,
            int commandImage, object bindings)
        {
            Command command = null;
            object[] contextUIGuids = {};

            try
            {
                command = _applicationObject.Commands.AddNamedCommand(_addInInstance, commandShortName, commandCaption,
                    commandTooltip, true,
                    commandImage, ref contextUIGuids, (int) vsCommandStatus.vsCommandStatusSupported);

                if (bindings != null)
                {
                    command.Bindings = bindings;
                }
            }
            catch (Exception)
            {
                // This should not happen!
            }
        }

        /// <summary>
        ///     Initializes the add in.
        /// </summary>
        private void InitializeAddIn()
        {
            // Retrieve the context menu of Graph window in a LoadTest.
            var testResultsCommandBars = (CommandBars) _applicationObject.CommandBars;
            CommandBar loadTestResultsContextMenuCommandBar = testResultsCommandBars["Test Results Context Menu"];

            // Add a CommandBarPopup Control to the LoadTest Graph Window Command Bar. This control's caption will be 'Modify Report'
            testResultsContextMenuCommandBarControl =
                loadTestResultsContextMenuCommandBar.Controls.Add(MsoControlType.msoControlPopup, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing);
            testResultsContextMenuCommandBarControl.Caption = "Change Report Details";
            testResultsContextMenuCommandBarPopup = (CommandBarPopup) testResultsContextMenuCommandBarControl;
            testResultsContextMenuCommandBarPopup.BeginGroup = true;

            // Create the buttons from the commands
            AddCommandBarButton(testResultsContextMenuCommandBarPopup, reportName, reportImageIndex);
            AddCommandBarButton(testResultsContextMenuCommandBarPopup, settingsName, settingsImageIndex);
        }

        /// <summary>
        ///     Adds the command bar button.
        /// </summary>
        /// <param name="contextMenuCommandBarPopup">The command bar popup.</param>
        /// <param name="commandShortName">Short name of the command.</param>
        private void AddCommandBarButton(CommandBarPopup contextMenuCommandBarPopup, string commandShortName,
            int imageIndex)
        {
            Command command = null;
            CommandBarControl commandBarControl = null;
            CommandBarButton commandBarButton = null;

            try
            {
                // Try to retrieve the command, just in case it was already created, ignoring the 
                // exception that would happen if the command was not created yet.
                command = _applicationObject.Commands.Item(_addInInstance.ProgID + "." + commandShortName);
            }
            catch
            {
            }

            // Add the command if it does not exist
            if (command == null)
            {
                object[] contextUIGuids = {};
                command = _applicationObject.Commands.AddNamedCommand(_addInInstance, commandShortName, commandShortName,
                    commandShortName, true, imageIndex, ref contextUIGuids,
                    (int) vsCommandStatus.vsCommandStatusSupported);
            }

            // Add a control to the command bar
            commandBarControl =
                (CommandBarControl)
                    command.AddControl(contextMenuCommandBarPopup.CommandBar,
                        contextMenuCommandBarPopup.Controls.Count + 1);

            // Cast it to CommandBarButton to set some properties
            commandBarButton = (CommandBarButton) commandBarControl;
            commandBarButton.BeginGroup = true;
            commandBarButton.Style = MsoButtonStyle.msoButtonIconAndCaption;
            commandBarButton.Visible = true;
        }

        /// <summary>
        ///     Recreates the commands.
        /// </summary>
        private void RecreateCommands()
        {
            RecreateCommand(reportName, reportName, reportName, reportImageIndex);
            RecreateCommand(settingsName, settingsName, settingsName, settingsImageIndex);
        }

        /// <summary>
        ///     Recreates the command.
        /// </summary>
        /// <param name="commandShortName">Short name of the command.</param>
        /// <param name="commandCaption">The command caption.</param>
        /// <param name="commandTooltip">The command tooltip.</param>
        /// <param name="commandImage">The command image.</param>
        private void RecreateCommand(string commandShortName, string commandCaption, string commandTooltip,
            int commandImage)
        {
            Command existingCommand = null;
            object bindings = null;
            string commandFullName = null;

            commandFullName = _addInInstance.ProgID + "." + commandShortName;

            // Try to get the command if it exists to get the current binding
            try
            {
                existingCommand = _applicationObject.Commands.Item(commandFullName, -1);
            }
            catch
            {
            }

            if (existingCommand == null)
            {
                // This should not happen!
            }
            else
            {
                // We must preserve the command bindings
                bindings = existingCommand.Bindings;

                // Remove the existing command
                existingCommand.Delete();

                // Create it again
                CreateCommand(commandShortName, commandCaption, commandTooltip, commandImage, bindings);
            }
        }

        /// <summary>
        ///     Shows the message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="messageType">Type of the message.</param>
        private void ShowMessage(string message, int messageType = 0)
        {
            switch (messageType)
            {
                case 0:
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                case 1:
                    MessageBox.Show(message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    break;
                case 2:
                    MessageBox.Show(message, "Stop", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    break;
                case 3:
                    MessageBox.Show(message, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    break;
                default:
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
            }
        }

        /// <summary>
        ///     Gets the TRX files.
        /// </summary>
        /// <returns></returns>
        private List<TrxFile> GetTrxFiles()
        {
            var resultFiles = new List<TrxFile>();

            IServiceProvider serviceProvider =
                new ServiceProvider((Microsoft.VisualStudio.OLE.Interop.IServiceProvider) _applicationObject);
            var TestManagement = serviceProvider.GetService(typeof (STestManagement)) as ITestManagement;
            if (TestManagement != null)
            {
                ITmi TmiInst = TestManagement.TmiInstance;
                if (TmiInst != null)
                {
                    TestRun[] testRuns = TmiInst.GetTestRuns();

                    if (testRuns.Length > 0)
                    {
                        foreach (TestRun run in testRuns)
                        {
                            resultFiles.Add(new TrxFile(run.ResultFilePath, run.ResultFileName));
                        }
                    }
                }
            }

            return resultFiles;
        }

        #endregion
    }
}